package fetcher

import (
	"crypto/sha256"
	"encoding/hex"
	"io"
	"os"
	"path/filepath"
	"sync"
)

type Cache struct {
	dir string
	mu  sync.Mutex
}

func NewCache(dir string) *Cache {
	os.MkdirAll(dir, 0755)
	return &Cache{dir: dir}
}

func (c *Cache) path(hash string) string {
	if len(hash) < 4 {
		return filepath.Join(c.dir, hash)
	}
	return filepath.Join(c.dir, hash[:2], hash[2:4], hash)
}

func (c *Cache) Get(hash string) ([]byte, bool) {
	p := c.path(hash)
	data, err := os.ReadFile(p)
	if err != nil {
		return nil, false
	}
	return data, true
}

func (c *Cache) Put(hash string, data []byte) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	p := c.path(hash)
	os.MkdirAll(filepath.Dir(p), 0755)

	// verify hash correctness before storing
	h := sha256.Sum256(data)
	if hex.EncodeToString(h[:]) != hash {
		return io.ErrUnexpectedEOF
	}

	return os.WriteFile(p, data, 0644)
}

func (c *Cache) Size() int64 {
	var size int64
	filepath.Walk(c.dir, func(_ string, info os.FileInfo, err error) error {
		if err == nil && !info.IsDir() {
			size += info.Size()
		}
		return nil
	})
	return size
}
